home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / DirectX / dxsdk_oct2004.exe / dxsdk.exe / Samples / C++ / DirectInput / DIConfig / cdeviceview.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2004-09-27  |  19.5 KB  |  858 lines

  1. //-----------------------------------------------------------------------------
  2. // File: cdeviceview.cpp
  3. //
  4. // Desc: CDeviceView is a window class derived from CFlexWnd.  It represents
  5. //       the device view window in which the device and callouts are drawn.
  6. //       Each CDeviceView only represents one view.  A device that has more
  7. //       than one view should have a corresponding number of CDeviceView for it.
  8. //
  9. // Copyright (C) Microsoft Corporation. All Rights Reserved.
  10. //-----------------------------------------------------------------------------
  11.  
  12. #include "common.hpp"
  13.  
  14.  
  15. CDeviceView::CDeviceView(CDeviceUI &ui) :
  16.     m_ui(ui),
  17.     m_pbmImage(NULL),
  18.     m_pbmThumb(NULL),
  19.     m_pbmSelThumb(NULL),
  20.     m_SuperState(0),
  21.     m_State(0),
  22.     m_SubState(0),
  23.     m_OldSuperState(0),
  24.     m_OldState(0),
  25.     m_OldSubState(0),
  26.     m_pControlContext(NULL),
  27.     m_ptszImagePath(NULL),
  28.     m_bScrollEnable(FALSE),
  29.     m_nScrollOffset(0),
  30.     m_nViewHeight(g_sizeImage.cy),
  31.     m_bForcePaint(FALSE)
  32. {
  33.     m_ptNextWLOText.x = m_ptNextWLOText.y = 0;
  34. }
  35.  
  36. CDeviceView::~CDeviceView()
  37. {
  38.     Unpopulate();
  39. }
  40.  
  41. CDeviceControl *CDeviceView::NewControl()
  42. {
  43.     CDeviceControl *pControl = new CDeviceControl(m_ui, *this);
  44.     if (!pControl)
  45.         return NULL;
  46.     m_arpControl.SetAtGrow(m_arpControl.GetSize(), pControl);
  47.     return pControl;
  48. }
  49.  
  50. void CDeviceView::Remove(CDeviceControl *pControl)
  51. {
  52.     if (pControl == NULL)
  53.         return;
  54.  
  55.     int i = pControl->GetControlIndex();
  56.     if (i < 0 || i >= GetNumControls())
  57.     {
  58.         assert(0);
  59.         return;
  60.     }
  61.  
  62.     if (pControl == m_pControlContext)
  63.         m_pControlContext = NULL;
  64.  
  65.     if (m_arpControl[i] != NULL)
  66.         delete m_arpControl[i];
  67.     m_arpControl[i] = NULL;
  68.  
  69.     m_arpControl.RemoveAt(i);
  70.  
  71.     Invalidate();
  72. }
  73.  
  74. void CDeviceView::RemoveAll(BOOL bUser)
  75. {
  76.     m_pControlContext = NULL;
  77.  
  78.     for (int i = 0; i < GetNumControls(); i++)
  79.     {
  80.         if (m_arpControl[i] != NULL)
  81.             delete m_arpControl[i];
  82.         m_arpControl[i] = NULL;
  83.     }
  84.     m_arpControl.RemoveAll();
  85.  
  86.     Invalidate();
  87. }
  88.  
  89. void CDeviceView::Unpopulate(BOOL bInternalOnly)
  90. {
  91.     DisableScrollBar();
  92.  
  93.     m_bScrollEnable = FALSE;
  94.  
  95.     if (m_pbmImage != NULL)
  96.         delete m_pbmImage;
  97.     if (m_pbmThumb != NULL)
  98.         delete m_pbmThumb;
  99.     if (m_pbmSelThumb != NULL)
  100.         delete m_pbmSelThumb;
  101.     m_pbmImage = NULL;
  102.     m_pbmThumb = NULL;
  103.     m_pbmSelThumb = NULL;
  104.     free(m_ptszImagePath);
  105.     m_ptszImagePath = NULL;
  106.  
  107.     if (!bInternalOnly)
  108.         RemoveAll(FALSE);
  109.  
  110.     for (int i = 0; i < m_arpText.GetSize(); i++)
  111.     {
  112.         if (m_arpText[i])
  113.             delete m_arpText[i];
  114.         m_arpText[i] = NULL;
  115.     }
  116.     m_arpText.RemoveAll();
  117. }
  118.  
  119. void AssureSize(CBitmap *&pbm, SIZE to)
  120. {
  121.     if (!pbm)
  122.         return;
  123.  
  124.     SIZE from;
  125.     if (!pbm->GetSize(&from))
  126.         return;
  127.  
  128.     if (from.cx >= to.cx && from.cy >= to.cy)
  129.         return;
  130.  
  131.     CBitmap *nbm = CBitmap::Create(to, RGB(0,0,0));
  132.     if (!nbm)
  133.         return;
  134.  
  135.     HDC hDC = nbm->BeginPaintInto();
  136.     pbm->Draw(hDC);
  137.     nbm->EndPaintInto(hDC);
  138.  
  139.     delete pbm;
  140.     pbm = nbm;
  141.     nbm = NULL;
  142. }
  143.  
  144. CBitmap *CDeviceView::GrabViewImage()
  145. {
  146.     CBitmap *pbm = CBitmap::Create(GetClientSize(), RGB(0, 0, 0), NULL);
  147.     if (!pbm)
  148.         return NULL;
  149.     HDC hDC = pbm->BeginPaintInto();
  150.     if (!hDC)
  151.     {
  152.         delete pbm;
  153.         return NULL;
  154.     }
  155.  
  156.     OnPaint(hDC);
  157.  
  158.     pbm->EndPaintInto(hDC);
  159.  
  160.     return pbm;
  161. }
  162.  
  163. void CDeviceView::MakeMissingImages()
  164. {
  165. //    if (m_pbmImage)
  166. //        AssureSize(m_pbmImage, g_sizeImage);
  167.  
  168.     if (m_pbmThumb == NULL)
  169.     {
  170.         if (m_pbmImage)
  171.             m_pbmThumb = m_pbmImage->CreateResizedTo(g_sizeThumb);
  172.         else
  173.         {
  174.             CBitmap *pbmImage = GrabViewImage();
  175.             if (pbmImage)
  176.             {
  177.                 AssureSize(pbmImage, g_sizeImage);
  178.                 m_pbmThumb = pbmImage->CreateResizedTo(g_sizeThumb);
  179.             }
  180.             delete pbmImage;
  181.         }
  182.     }
  183.  
  184.     if (m_pbmThumb == NULL)
  185.         return;
  186.  
  187.     if (m_pbmSelThumb == NULL)
  188.     {
  189.         m_pbmSelThumb = m_pbmThumb->Dup();
  190.         if (m_pbmSelThumb != NULL)
  191.         {
  192.             HDC hDC = m_pbmSelThumb->BeginPaintInto();
  193.             {
  194.                 CPaintHelper ph(m_ui.m_uig, hDC);
  195.                 ph.SetPen(UIP_SELTHUMB);
  196.                 ph.Rectangle(0, 0, g_sizeThumb.cx, g_sizeThumb.cy, UIR_OUTLINE);
  197.             }
  198.             m_pbmSelThumb->EndPaintInto(hDC);
  199.         }
  200.     }
  201. }
  202.  
  203. void CDeviceView::OnPaint(HDC hDC)
  204. {
  205.     HDC hBDC = NULL, hODC = NULL;
  206.     CBitmap *pbm = NULL;
  207.  
  208.     if (!InRenderMode())
  209.     {
  210.         hODC = hDC;
  211.         pbm = CBitmap::Create(GetClientSize(), RGB(0, 0, 0), hDC);
  212.         if (pbm != NULL)
  213.         {
  214.             hBDC = pbm->BeginPaintInto();
  215.             if (hBDC != NULL)
  216.                 hDC = hBDC;
  217.         }
  218.     }
  219.  
  220.     // Black-fill first
  221.     SIZE fillsz = GetClientSize();
  222.     RECT fillrc = {0, 0, fillsz.cx, fillsz.cy};
  223.     FillRect(hDC, &fillrc, (HBRUSH)GetStockObject(BLACK_BRUSH));
  224.  
  225.     if (m_pbmImage != NULL)
  226.         m_pbmImage->Blend(hDC);
  227.  
  228.     BOOL bScroll = m_bScrollEnable && m_sb.m_hWnd;
  229.     int sdc = 0;
  230.     if (bScroll)
  231.     {
  232.         sdc = SaveDC(hDC);
  233.         OffsetViewportOrgEx(hDC, 0, -m_nScrollOffset + g_iListHeaderHeight, NULL);
  234.     }
  235.     else
  236.     if (m_bScrollEnable)
  237.     {
  238.         sdc = SaveDC(hDC);
  239.         OffsetViewportOrgEx(hDC, 0, g_iListHeaderHeight, NULL);
  240.     }
  241.  
  242.     int miny = 0 + m_nScrollOffset;
  243.     int maxy = g_sizeImage.cy + m_nScrollOffset;
  244.  
  245.     int t, nt = GetNumTexts();
  246.     for (t = 0; t < nt; t++)
  247.     {
  248.         CDeviceViewText *pText = m_arpText[t];
  249.         if (pText != NULL &&
  250.             !(pText->GetMinY() > maxy || pText->GetMaxY() < miny))
  251.                 pText->OnPaint(hDC);
  252.     }
  253.  
  254.     BOOL bCFGUIEdit = m_ui.m_uig.InEditMode();
  255.     BOOL bEitherEditMode = bCFGUIEdit;
  256.  
  257.     int c, nc = GetNumControls();
  258.     for (c = 0; c < nc; c++)
  259.         if (m_arpControl[c] != NULL && m_arpControl[c]->HasOverlay() &&
  260.             (m_arpControl[c]->IsHighlighted()
  261.                 )
  262.                 && (bEitherEditMode || m_arpControl[c]->IsMapped()))
  263.             m_arpControl[c]->DrawOverlay(hDC);
  264.     for (c = 0; c < nc; c++)
  265.     {
  266.         CDeviceControl *pControl = m_arpControl[c];
  267.         if (pControl != NULL && (bEitherEditMode || pControl->IsMapped()) &&
  268.             !(pControl->GetMinY() > maxy || pControl->GetMaxY() < miny))
  269.             pControl->OnPaint(hDC);
  270.     }
  271.  
  272.     if (bScroll || m_bScrollEnable)
  273.     {
  274.         RestoreDC(hDC, sdc);
  275.         sdc = 0;
  276.     }
  277.  
  278.     // Black fill the top portion if this is a list view
  279.     if (bScroll)
  280.     {
  281.         GetClientRect(&fillrc);
  282.         fillrc.bottom = g_iListHeaderHeight;
  283.         FillRect(hDC, &fillrc, (HBRUSH)GetStockObject(BLACK_BRUSH));
  284.     }
  285.  
  286.     // Print out the headers
  287.     TCHAR tszHeader[MAX_PATH];
  288.     // Control column
  289.     if (m_arpText.GetSize())
  290.     {
  291.         CPaintHelper ph(m_ui.m_uig, hDC);
  292.         ph.SetElement(UIE_CALLOUT);
  293.  
  294.         for (int i = 0; i < 2; i++)
  295.         {
  296.             // Check if there are two columns, break out the 2nd iteration if not 2 columns.
  297.             if (i == 1 && !(GetNumControls() > 1 &&
  298.                 m_arpControl[0]->GetCalloutMaxRect().top == m_arpControl[1]->GetCalloutMaxRect().top))
  299.                 break;
  300.  
  301.             RECT rcheader;
  302.             if (m_arpText.GetSize())
  303.             {
  304.                 rcheader = m_arpText[i]->GetRect();
  305.                 rcheader.bottom -= rcheader.top;
  306.                 rcheader.top = 0;
  307.                 LoadString(g_hModule, IDS_LISTHEADER_CTRL, tszHeader, MAX_PATH);
  308.                 DrawText(hDC, tszHeader, -1, &rcheader, DT_LEFT|DT_NOPREFIX|DT_CALCRECT);
  309.                 if (rcheader.right > m_arpText[i]->GetRect().right)
  310.                     rcheader.left -= rcheader.right - m_arpText[i]->GetRect().right;
  311.                 DrawText(hDC, tszHeader, -1, &rcheader, DT_LEFT|DT_NOPREFIX);
  312.  
  313.                 // Action column
  314.                 rcheader = m_arpControl[i]->GetCalloutMaxRect();
  315.                 rcheader.bottom -= rcheader.top;
  316.                 rcheader.top = 0;
  317.                 LoadString(g_hModule, IDS_LISTHEADER_ACTION, tszHeader, MAX_PATH);
  318.                 DrawText(hDC, tszHeader, -1, &rcheader, DT_CENTER|DT_NOPREFIX);
  319.             }
  320.         }
  321.     }
  322.  
  323.  
  324.     if (!InRenderMode())
  325.     {
  326.         if (pbm != NULL)
  327.         {
  328.             if (hBDC != NULL)
  329.             {
  330.                 pbm->EndPaintInto(hBDC);
  331.                 pbm->Draw(hODC);
  332.             }
  333.             delete pbm;
  334.         }
  335.     }
  336. }
  337.  
  338. int CDeviceView::GetNumControls()
  339. {
  340.     return m_arpControl.GetSize();
  341. }
  342.  
  343. CDeviceControl *CDeviceView::GetControl(int nControl)
  344. {
  345.     if (nControl >= 0 && nControl < GetNumControls())
  346.         return m_arpControl[nControl];
  347.     else
  348.         return NULL;
  349. }
  350.  
  351. CBitmap *CDeviceView::GetImage(DVIMAGE dvi)
  352. {
  353.     switch (dvi)
  354.     {
  355.         case DVI_IMAGE: return m_pbmImage;
  356.         case DVI_THUMB: return m_pbmThumb;
  357.         case DVI_SELTHUMB: return m_pbmSelThumb;
  358.  
  359.         default:
  360.             return NULL;
  361.     }
  362. }
  363.  
  364. void CDeviceView::OnMouseOver(POINT point, WPARAM wParam)
  365. {
  366.     if (m_bScrollEnable && m_sb.m_hWnd)
  367.         point.y += m_nScrollOffset;
  368.  
  369.  
  370.     // Check if we are over a control
  371.     POINT adjPt = point;
  372.     if (m_bScrollEnable) adjPt.y -= g_iListHeaderHeight;
  373.     int c, nc = GetNumControls();
  374.     for (c = 0; c < nc; c++)
  375.         if (m_arpControl[c] != NULL && m_arpControl[c]->HitTest(adjPt) != DCHT_NOHIT)
  376.         {
  377.             m_arpControl[c]->OnMouseOver(adjPt);
  378.             return;
  379.         }
  380.  
  381.     // Check if we are over a viewtext
  382.     nc = GetNumTexts();
  383.     for (c = 0; c < nc; c++)
  384.         if (m_arpText[c] != NULL && m_arpText[c]->HitTest(adjPt) != DCHT_NOHIT)
  385.         {
  386.             m_arpText[c]->OnMouseOver(adjPt);
  387.             return;
  388.         }
  389.  
  390.     CFlexWnd::s_ToolTip.SetEnable(FALSE);
  391.  
  392.     DEVICEUINOTIFY uin;
  393.     uin.msg = DEVUINM_MOUSEOVER;
  394.     uin.from = DEVUINFROM_VIEWWND;
  395.     uin.mouseover.point = point;
  396.     m_ui.Notify(uin);
  397. }
  398.  
  399. void CDeviceView::OnClick(POINT point, WPARAM wParam, BOOL bLeft)
  400. {
  401.     if (m_bScrollEnable && m_sb.m_hWnd)
  402.         point.y += m_nScrollOffset;
  403.  
  404.  
  405.     POINT adjPt = point;
  406.     if (m_bScrollEnable) adjPt.y -= g_iListHeaderHeight;
  407.     int c, nc = GetNumControls();
  408.     for (c = 0; c < nc; c++)
  409.         // adjPt is the adjust click point for scrolling list view
  410.         if (m_arpControl[c] != NULL && m_arpControl[c]->HitTest(adjPt) != DCHT_NOHIT)
  411.         {
  412.             m_arpControl[c]->OnClick(adjPt, bLeft);
  413.             return;
  414.         }
  415.  
  416.     {
  417.         for (c = 0; c < GetNumTexts(); ++c)
  418.             if (m_arpControl[c] != NULL && m_arpText[c] != NULL)
  419.             {
  420.                 RECT rc = m_arpText[c]->GetRect();
  421.                 if (PtInRect(&rc, adjPt))
  422.                 {
  423.                     m_arpControl[c]->OnClick(adjPt, bLeft);
  424.                     return;
  425.                 }
  426.             }
  427.     }
  428.  
  429.  
  430.     // Send notification
  431.     DEVICEUINOTIFY uin;
  432.     uin.msg = DEVUINM_CLICK;
  433.     uin.from = DEVUINFROM_VIEWWND;
  434.     uin.click.bLeftButton = bLeft;
  435.     m_ui.Notify(uin);
  436. }
  437.  
  438. void CDeviceView::OnDoubleClick(POINT point, WPARAM wParam, BOOL bLeft)
  439. {
  440.     if (m_bScrollEnable && m_sb.m_hWnd)
  441.         point.y += m_nScrollOffset;
  442.  
  443.     POINT adjPt = point;
  444.     if (m_bScrollEnable) adjPt.y -= g_iListHeaderHeight;
  445.     int c, nc = GetNumControls();
  446.     for (c = 0; c < nc; c++)
  447.         if (m_arpControl[c] != NULL && m_arpControl[c]->HitTest(adjPt) != DCHT_NOHIT)
  448.         {
  449.             m_arpControl[c]->OnClick(adjPt, bLeft, TRUE);
  450.             return;
  451.         }
  452.  
  453.     for (c = 0; c < GetNumTexts(); ++c)
  454.         if (m_arpControl[c] != NULL && m_arpText[c] != NULL)
  455.         {
  456.             RECT rc = m_arpText[c]->GetRect();
  457.             if (PtInRect(&rc, adjPt))
  458.             {
  459.                 m_arpControl[c]->OnClick(adjPt, bLeft, TRUE);
  460.                 return;
  461.             }
  462.         }
  463.  
  464.     DEVICEUINOTIFY uin;
  465.     uin.msg = DEVUINM_DOUBLECLICK;
  466.     uin.from = DEVUINFROM_VIEWWND;
  467.     uin.click.bLeftButton = bLeft;
  468.     m_ui.Notify(uin);
  469. }
  470.  
  471. void CDeviceView::OnWheel(POINT point, WPARAM wParam)
  472. {
  473.     if (!m_bScrollEnable) return;
  474.  
  475.     if (m_sb.GetMin() == m_sb.GetMax()) return;
  476.  
  477.     int nPage = MulDiv(m_sb.GetPage(), 9, 10) >> 1;  // Half a page at a time
  478.  
  479.     if ((int)wParam >= 0)
  480.         m_sb.AdjustPos(-nPage);
  481.     else
  482.         m_sb.AdjustPos(nPage);
  483.  
  484.     m_nScrollOffset = m_sb.GetPos();
  485.     Invalidate();
  486. }
  487.  
  488.  
  489. BOOL CDeviceView::DoesCalloutExistForOffset(DWORD dwOfs)
  490. {
  491.     return DoesCalloutOtherThanSpecifiedExistForOffset(NULL, dwOfs);
  492. }
  493.  
  494. BOOL CDeviceView::DoesCalloutOtherThanSpecifiedExistForOffset(CDeviceControl *pOther, DWORD dwOfs)
  495. {
  496.     int nc = GetNumControls();
  497.     for (int i = 0; i < nc; i++)
  498.     {
  499.         CDeviceControl *pControl = GetControl(i);
  500.         if (pControl == NULL || pControl == pOther)
  501.             continue;
  502.         if (!pControl->IsOffsetAssigned())
  503.             continue;
  504.         if (pControl->GetOffset() == dwOfs)
  505.             return TRUE;
  506.     }
  507.     return FALSE;
  508. }
  509.  
  510. // This function returns the index of a control with the specified offset
  511. int CDeviceView::GetIndexFromOfs(DWORD dwOfs)
  512. {
  513.     for (int i = 0; i < GetNumControls(); ++i)
  514.         if (m_arpControl[i]->GetOffset() == dwOfs)
  515.             return i;
  516.  
  517.     return -1;
  518. }
  519.  
  520.  
  521. int CDeviceView::GetViewIndex()
  522. {
  523.     return m_ui.GetViewIndex(this);
  524. }
  525.  
  526.  
  527. BOOL CDeviceView::IsUnassignedOffsetAvailable()
  528. {
  529.     DIDEVOBJSTRUCT os;
  530.  
  531.     HRESULT hr = FillDIDeviceObjectStruct(os, m_ui.m_lpDID);
  532.     if (FAILED(hr))
  533.         return FALSE;
  534.  
  535.     if (os.nObjects < 1)
  536.         return FALSE;
  537.  
  538.     assert(os.pdoi);
  539.     if (!os.pdoi)
  540.         return FALSE;
  541.  
  542.     for (int i = 0; i < os.nObjects; i++)
  543.     {
  544.         const DIDEVICEOBJECTINSTANCEW &o = os.pdoi[i];
  545.  
  546.         if (!DoesCalloutExistForOffset(o.dwOfs))
  547.             return TRUE;
  548.     }
  549.  
  550.     return FALSE;
  551. }
  552.  
  553. CDeviceViewText *CDeviceView::AddText(
  554.     HFONT f, COLORREF t, COLORREF b, const RECT &r, LPCTSTR text)
  555. {
  556.     CDeviceViewText *pText = NewText();
  557.     if (!pText)
  558.         return NULL;
  559.  
  560.     pText->SetLook(f, t, b);
  561.     pText->SetRect(r);
  562.     pText->SetText(text);
  563.  
  564.     return pText;
  565. }
  566.  
  567. CDeviceViewText *CDeviceView::AddText(
  568.     HFONT f, COLORREF t, COLORREF b, const POINT &p, LPCTSTR text)
  569. {
  570.     CDeviceViewText *pText = NewText();
  571.     if (!pText)
  572.         return NULL;
  573.  
  574.     pText->SetLook(f, t, b);
  575.     pText->SetPosition(p);
  576.     pText->SetTextAndResizeTo(text);
  577.  
  578.     return pText;
  579. }
  580.  
  581. CDeviceViewText *CDeviceView::AddWrappedLineOfText(
  582.     HFONT f, COLORREF t, COLORREF b, LPCTSTR text)
  583. {
  584.     CDeviceViewText *pText = NewText();
  585.     if (!pText)
  586.         return NULL;
  587.  
  588.     pText->SetLook(f, t, b);
  589.     pText->SetPosition(m_ptNextWLOText);
  590.     pText->SetTextAndResizeToWrapped(text);
  591.     
  592.     m_ptNextWLOText.y += pText->GetHeight();
  593.  
  594.     return pText;
  595. }
  596.  
  597. CDeviceViewText *CDeviceView::NewText()
  598. {
  599.     CDeviceViewText *pText = new CDeviceViewText(m_ui, *this);
  600.     if (!pText)
  601.         return NULL;
  602.     m_arpText.SetAtGrow(m_arpText.GetSize(), pText);
  603.     return pText;
  604. }
  605.  
  606.  
  607. int CDeviceView::GetNumTexts()
  608. {
  609.     return m_arpText.GetSize();
  610. }
  611.  
  612. CDeviceViewText *CDeviceView::GetText(int nText)
  613. {
  614.     if (nText < 0 || nText >= GetNumTexts())
  615.         return NULL;
  616.     return m_arpText[nText];
  617. }
  618.  
  619. void CDeviceView::SetImage(CBitmap *&refpbm)
  620. {
  621.     delete m_pbmImage;
  622.     m_pbmImage = refpbm;
  623.     refpbm = NULL;
  624.     MakeMissingImages();
  625.     Invalidate();
  626. }
  627.  
  628. void CDeviceView::SetImagePath(LPCTSTR tszPath)
  629. {
  630.     if (m_ptszImagePath)
  631.         free(m_ptszImagePath);
  632.     m_ptszImagePath = NULL;
  633.  
  634.     if (tszPath)
  635.         m_ptszImagePath = _tcsdup(tszPath);
  636. }
  637.  
  638. void CDeviceView::CalcDimensions()
  639. {
  640.     // go through all texts and controls to find the max y coord
  641.     int max = g_sizeImage.cy - g_iListHeaderHeight;
  642.     int i = 0;
  643.     for (; i < GetNumTexts(); i++)
  644.     {
  645.         CDeviceViewText *pText = GetText(i);
  646.         if (!pText)
  647.             continue;
  648.         int ty = pText->GetMaxY();
  649.         if (ty > max)
  650.             max = ty;
  651.     }
  652.     for (i = 0; i < GetNumControls(); i++)
  653.     {
  654.         CDeviceControl *pControl = GetControl(i);
  655.         if (!pControl)
  656.             continue;
  657.         int cy = pControl->GetMaxY();
  658.         if (cy > max)
  659.             max = cy;
  660.     }
  661.  
  662.     // set
  663.     m_nViewHeight = max;
  664.     m_nScrollOffset = 0;
  665.  
  666.     // enable scrollbar if view height more than window size
  667.     if (m_nViewHeight > g_sizeImage.cy - g_iListHeaderHeight)
  668.         EnableScrollBar();
  669. }
  670.  
  671. void CDeviceView::DisableScrollBar()
  672. {
  673.     if (!m_sb.m_hWnd)
  674.         return;
  675.  
  676.     m_sb.Destroy();
  677. }
  678.  
  679. void CDeviceView::EnableScrollBar()
  680. {
  681.     if (m_sb.m_hWnd)
  682.         return;
  683.  
  684.     FLEXSCROLLBARCREATESTRUCT cs;
  685.     cs.dwSize = sizeof(cs);
  686.     cs.dwFlags = FSBF_VERT;
  687.     cs.min = 0;
  688.     cs.max = m_nViewHeight;
  689.     cs.page = g_sizeImage.cy - g_iListHeaderHeight;
  690.     cs.pos = m_nScrollOffset;
  691.     cs.hWndParent = m_hWnd;
  692.     cs.hWndNotify = m_hWnd;
  693.     RECT rect = {g_sizeImage.cx - DEFAULTVIEWSBWIDTH, g_iListHeaderHeight, g_sizeImage.cx, g_sizeImage.cy};
  694.     cs.rect = rect;
  695.     cs.bVisible = TRUE;
  696.     m_sb.SetColors(
  697.         m_ui.m_uig.GetBrushColor(UIE_SBTRACK),
  698.         m_ui.m_uig.GetBrushColor(UIE_SBTHUMB),
  699.         m_ui.m_uig.GetPenColor(UIE_SBBUTTON));
  700.     m_sb.Create(&cs);
  701. }
  702.  
  703. LRESULT CDeviceView::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  704. {
  705.     switch (msg)
  706.     {
  707.         case WM_PAINT:
  708.             m_bForcePaint = TRUE;
  709.             return CFlexWnd::WndProc(hWnd, msg, wParam, lParam);
  710.  
  711.         case WM_FLEXVSCROLL:
  712.         {
  713.             int code = (int)wParam;
  714.             CFlexScrollBar *pSB = (CFlexScrollBar *)lParam;
  715.             if (!pSB)
  716.                 return 0;
  717.  
  718.             int nLine = 5;
  719.             int nPage = MulDiv(pSB->GetPage(), 9, 10);
  720.  
  721.             switch (code)
  722.             {
  723.                 case SB_LINEUP: pSB->AdjustPos(-nLine); break;
  724.                 case SB_LINEDOWN: pSB->AdjustPos(nLine); break;
  725.                 case SB_PAGEUP: pSB->AdjustPos(-nPage); break;
  726.                 case SB_PAGEDOWN: pSB->AdjustPos(nPage); break;
  727.                 case SB_THUMBTRACK: pSB->SetPos(pSB->GetThumbPos()); break;
  728.             }
  729.  
  730.             m_nScrollOffset = pSB->GetPos();
  731.  
  732.             Invalidate();
  733.             return 0;
  734.         }
  735.  
  736.         case WM_FLEXHSCROLL:
  737.             assert(0);
  738.         default:
  739.             return CFlexWnd::WndProc(hWnd, msg, wParam, lParam);
  740.     }
  741. }
  742.  
  743. void CDeviceView::ScrollToMakeControlVisible(const RECT &rc)
  744. {
  745.     RECT viewrc;
  746.  
  747.     if (!m_bScrollEnable)
  748.         return;
  749.  
  750.     GetClientRect(&viewrc);
  751.     viewrc.bottom -= g_iListHeaderHeight;
  752.     viewrc.top += m_nScrollOffset;
  753.     viewrc.bottom += m_nScrollOffset;
  754.  
  755.     // If scroll enabled, we scroll the view to make the control visible if not already so.
  756.     if (m_bScrollEnable && m_sb.m_hWnd &&
  757.         !(viewrc.left <= rc.left &&
  758.           viewrc.right >= rc.right &&
  759.           viewrc.top <= rc.top &&
  760.           viewrc.bottom >= rc.bottom))
  761.     {
  762.         // If the callout is below the view window, scroll so it shows up at the bottom of the window.
  763.         if (viewrc.bottom < rc.bottom)
  764.             m_sb.SetPos(m_sb.GetPos() + rc.bottom - viewrc.bottom);
  765.         else
  766.             m_sb.SetPos(rc.top);
  767.         m_nScrollOffset = m_sb.GetPos();
  768.         Invalidate();
  769.     }
  770. }
  771.  
  772. void CDeviceView::SwapControls(int i, int j)
  773. {
  774.     RECT rect;
  775.     CDeviceControl *pTmpControl;
  776.     CDeviceViewText *pTmpViewText;
  777.  
  778.     pTmpControl = m_arpControl[i];
  779.     m_arpControl[i] = m_arpControl[j];
  780.     m_arpControl[j] = pTmpControl;
  781.     pTmpViewText = m_arpText[i];
  782.     m_arpText[i] = m_arpText[j];
  783.     m_arpText[j] = pTmpViewText;
  784.     // Swap the rect back so everything will display properly.
  785.     rect = m_arpControl[i]->GetCalloutMaxRect();
  786.     m_arpControl[i]->SetCalloutMaxRect(m_arpControl[j]->GetCalloutMaxRect());
  787.     m_arpControl[j]->SetCalloutMaxRect(rect);
  788.     rect = m_arpText[i]->GetRect();
  789.     m_arpText[i]->SetRect(m_arpText[j]->GetRect());
  790.     m_arpText[j]->SetRect(rect);
  791.     // Exchange the text rect width, so the correct width stays with the correct text.
  792.     RECT rc1 = m_arpText[i]->GetRect();
  793.     RECT rc2 = m_arpText[j]->GetRect();
  794.     // Store rc1's new width first
  795.     int iTempWidth = rc1.right - (rc2.right - rc2.left);
  796.     rc2.left = rc2.right - (rc1.right - rc1.left);  // Adjust rc2's width
  797.     rc1.left = iTempWidth;  // Adjust rc1's width
  798.     m_arpText[i]->SetRect(rc1);
  799.     m_arpText[j]->SetRect(rc2);
  800. }
  801.  
  802. // Implements a simple selection sort algorithm to sort the control array and viewtext array.
  803. // - iStart is the starting index, inclusive.
  804. // - iEnd is the last index, exclusive.
  805. void CDeviceView::SortCallouts(int iStart, int iEnd)
  806. {
  807.     for (int i = iStart; i < iEnd - 1; ++i)
  808.     {
  809.         DWORD dwSmallestOfs = m_arpControl[i]->GetOffset();
  810.         int iSmallestIndex = i;
  811.         for (int j = i + 1; j < iEnd; ++j)
  812.             if (m_arpControl[j]->GetOffset() < dwSmallestOfs)
  813.             {
  814.                 dwSmallestOfs = m_arpControl[j]->GetOffset();
  815.                 iSmallestIndex = j;
  816.             }
  817.         // Swap the smallest element with i-th element.
  818.         if (iSmallestIndex != i)
  819.             SwapControls(i, iSmallestIndex);
  820.     }
  821. }
  822.  
  823. void CDeviceView::SortAssigned(BOOL bSort)
  824. {
  825.     // If less than 2 controls, no need for sorting.
  826.     if (m_arpControl.GetSize() < 2)
  827.         return;
  828.  
  829.     int iCalloutX[2] = {m_arpControl[0]->GetMinX(), m_arpControl[1]->GetMinX()};  // Callout X for the two columns
  830.  
  831.     // Sort the text array and control array.
  832.     if (bSort)
  833.     {
  834.         // First move all the assigned controls to the first n elements.
  835.         int iNextAssignedWriteIndex = 0;
  836.         for (int i = 0; i < m_arpControl.GetSize(); ++i)
  837.             if (m_arpControl[i]->HasAction())
  838.             {
  839.                 // Swap the controls
  840.                 SwapControls(i, iNextAssignedWriteIndex);
  841.                 ++iNextAssignedWriteIndex;  // Increment the write index
  842.             }
  843.  
  844.         // Sort the two parts now
  845.         SortCallouts(0, iNextAssignedWriteIndex);
  846.         SortCallouts(iNextAssignedWriteIndex, m_arpControl.GetSize());
  847.     } else
  848.         SortCallouts(0, m_arpControl.GetSize());
  849. }
  850.  
  851. void CDeviceView::DoOnPaint(HDC hDC)
  852. {
  853.     // Paint only if we have an update region.
  854.     if (GetUpdateRect(m_hWnd, NULL, FALSE) || m_bForcePaint)
  855.         OnPaint(hDC);
  856.     m_bForcePaint = FALSE;
  857. }
  858.